using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Media;
using System.Collections.Generic;
using MrDev.Graphics;
using IrrKlang;
using MrDev.GameObjects;

namespace MrDev
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class PlatformerGame : Microsoft.Xna.Framework.Game
    {
        public enum GameMusic
        {
            IntroMusic,
            InGameMusic,
            BossMusic
        }
        private delegate void DrawDelegate(GameTime gameTime);
        private delegate void UpdateDelegate(GameTime gameTime);
        private DrawDelegate drawDelegate;
        private DrawDelegate updateDelegate;
        private List<string> menuStringItems = new List<string>();
        private Texture2D background;
        private enum GameState
        {
            StartMenu,
            PlayingLevel,
            LevelComplete,
            InGameMenu,
            GameOver
        }

        // Resources for drawing.
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;
        private static SpriteFont font;
        private GameState state;

        // Meta-level game state.
        private int currentMenuIndex = 0;
        private Level level;
        private bool wasKeyPressed;

        // When the time remaining is less than the warning time, it blinks on the hud
        private static readonly TimeSpan WarningTime = TimeSpan.FromSeconds(30);

        private const int TargetFrameRate = 60;
        private const int BackBufferWidth = 20 * 48;
        private const int BackBufferHeight = 720;

        public static ISoundEngine Sound;
        private static ISound backgroundMusic;

        public PlatformerGame()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = BackBufferWidth;
            graphics.PreferredBackBufferHeight = BackBufferHeight;
            // Go full screen...
            //graphics.IsFullScreen = true;

            Content.RootDirectory = "Content";

            // Framerate differs between platforms.
            TargetElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / TargetFrameRate);

            Sound = new ISoundEngine();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = Content.Load<SpriteFont>("Fonts\\SpriteFont1");
            background = Content.Load<Texture2D>("Backgrounds\\background");
            SetStartMenu();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            switch (state)
            {
                case GameState.StartMenu:
                    updateDelegate = new DrawDelegate(UpdateMenu);
                    drawDelegate = new DrawDelegate(DrawMenu);
                    break;
                case GameState.PlayingLevel:
                    updateDelegate = new DrawDelegate(UpdatePlayingLevel);
                    drawDelegate = new DrawDelegate(DrawPlayingLevel);
                    break;
                case GameState.InGameMenu:
                    updateDelegate = new DrawDelegate(UpdateMenu);
                    drawDelegate = new DrawDelegate(DrawMenu);
                    break;
                case GameState.GameOver:
                    updateDelegate = new DrawDelegate(UpdateGameOver);
                    drawDelegate = new DrawDelegate(DrawGameOver);
                    break;
                case GameState.LevelComplete:
                    updateDelegate = new DrawDelegate(UpdateLevelComplete);
                    drawDelegate = new DrawDelegate(DrawLevelComplete);
                    break;
            }
            updateDelegate(gameTime);
            level.Update(gameTime);

            base.Update(gameTime);
        }

        private void UpdateMenu(GameTime gameTime)
        {
            level.Paused = true;
            KeyboardState keyboardState = Keyboard.GetState();
            bool continuePressed = keyboardState.IsKeyDown(Keys.Space) || keyboardState.IsKeyDown(Keys.Enter);
            bool up = keyboardState.IsKeyDown(Keys.Up);
            bool down = keyboardState.IsKeyDown(Keys.Down);
            if (!wasKeyPressed)
            {
                if (up || down)
                {
                    Sound.Play2D("sound\\menu.mp3");
                }
                if (continuePressed)
                {
                    Sound.Play2D("sound\\menuselect.mp3");
                    if (state == GameState.StartMenu)
                    {
                        if (currentMenuIndex == 0)
                        {
                            state = GameState.PlayingLevel;
                            StartBackgroundMusic(GameMusic.InGameMusic);
                            StartGame();
                        }
                        else if (currentMenuIndex == 1)
                        {
                            Exit();
                        }
                    }
                    else if (state == GameState.InGameMenu)
                    {
                        if (currentMenuIndex == 0)
                        {
                            state = GameState.PlayingLevel;
                        }
                        else if (currentMenuIndex == 1)
                        {
                            SetStartMenu();
                        }
                    }
                }
                else if (up)
                    currentMenuIndex = currentMenuIndex == 0 ? menuStringItems.Count - 1 : currentMenuIndex - 1;
                else if (down)
                    currentMenuIndex = (currentMenuIndex + 1) % menuStringItems.Count;
            }
            wasKeyPressed = keyboardState.GetPressedKeys().Length > 0;
            if (state != GameState.StartMenu && state != GameState.InGameMenu)
                level.Paused = false;

        }

        private void SetIngameMenu()
        {
            state = GameState.InGameMenu;
            currentMenuIndex = 0;
            menuStringItems.Clear();
            menuStringItems.Add("resume");
            menuStringItems.Add("quit");
        }

        private void SetStartMenu()
        {
            StartBackgroundMusic(GameMusic.IntroMusic);
            state = GameState.StartMenu;
            currentMenuIndex = 0;
            menuStringItems.Clear();
            menuStringItems.Add("start");
            menuStringItems.Add("exit");
            StartGame();
        }

        private void UpdatePlayingLevel(GameTime gameTime)
        {
            if (level.Player.GameOver)
            {
                state = GameState.GameOver;
                return;
            }

            KeyboardState keyboardState = Keyboard.GetState();
            bool escapePressed = keyboardState.IsKeyDown(Keys.Escape);
            bool continuePressed = keyboardState.GetPressedKeys().Length > 0 && !escapePressed;

            if (level.Boss.IsAlive == false)
            {
                state = GameState.LevelComplete;
            }
            if (!wasKeyPressed && escapePressed)
            {
                if (state == GameState.PlayingLevel)
                {
                    SetIngameMenu();
                }
                else
                {
                    SetStartMenu();
                }
            }


            if (!wasKeyPressed && continuePressed)
            {
                if (!level.Player.IsAlive)
                {
                    level.StartNewLife();
                    state = GameState.PlayingLevel;
                    StartBackgroundMusic(GameMusic.InGameMusic);
                }
            }

            wasKeyPressed = keyboardState.GetPressedKeys().Length > 0;
        }

        public static void StartBackgroundMusic(GameMusic gameMusic)
        {
            if (backgroundMusic != null)
                backgroundMusic.Stop();
            if (gameMusic == GameMusic.BossMusic)
            {
                backgroundMusic = Sound.Play2D("sound\\stopthewatch.xm", true);
            }
            else if (gameMusic == GameMusic.InGameMusic)
            {
                backgroundMusic = Sound.Play2D("sound\\chipsetsunset.xm", true);
            }
            else if (gameMusic == GameMusic.IntroMusic)
            {
                backgroundMusic = Sound.Play2D("sound\\artificial_sweetener.xm", true);
            }
            
        }

        private void UpdateGameOver(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            bool continuePressed = keyboardState.IsKeyDown(Keys.Space);

            if (!wasKeyPressed && continuePressed)
            {
                SetStartMenu();
            }

            wasKeyPressed = keyboardState.GetPressedKeys().Length > 0;
        }

        private void UpdateLevelComplete(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            bool continuePressed = keyboardState.IsKeyDown(Keys.Space);

            if (!wasKeyPressed && continuePressed)
            {
                SetStartMenu();
            }

            wasKeyPressed = keyboardState.GetPressedKeys().Length > 0;
        }

        private void StartGame()
        {
            // Find the path of the next level.
            string levelPath = @"Levels\Level1\LevelData.txt";
            levelPath = Path.Combine(StorageContainer.TitleLocation, "Content/" + levelPath);
            level = new Level(Services, levelPath);
        }

        /// <summary>
        /// Draws the game from background to foreground.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {

            graphics.GraphicsDevice.Clear(Color.LightBlue);
            float speedFactor = 0.5f;
            float maxScreenX = speedFactor * (float)((level.LevelSize.Width + level.SubLevelSize.Width) * Tile.Width);
            float maxScreenY = speedFactor * (float)((level.LevelSize.Height + level.SubLevelSize.Height) * Tile.Height);
            Matrix matrix = Matrix.CreateTranslation(speedFactor * -level.ScreenX, speedFactor * -level.ScreenY, 0);
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.None, matrix);
            spriteBatch.Draw(background, new Rectangle(0, 0, (int)maxScreenX, (int)maxScreenY), new Color(Color.White, 0.2f));
            spriteBatch.End();

            matrix = Matrix.CreateTranslation((int)-level.ScreenX, (int)-level.ScreenY, 0);
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.None, matrix);
            level.Draw(gameTime, spriteBatch);
            spriteBatch.End();
            drawDelegate(gameTime);
            base.Draw(gameTime);
            //DrawDebugInfo();
        }

        private void DrawDebugInfo()
        {
            spriteBatch.Begin();
            int line = 0;
            int lineSpacing = 40;
            int yOffset = 100;
            int xOffset = 10;
            DrawShadowedString(spriteBatch, "Player debug info:", new Vector2(xOffset, yOffset + (line++) * lineSpacing), Color.White);
            DrawShadowedString(spriteBatch, string.Format("Position: {0:####},{1}", level.Player.Parameters.Position.X, level.Player.Parameters.Position.Y), new Vector2(xOffset, yOffset + (line++) * lineSpacing), Color.White);
            DrawShadowedString(spriteBatch, string.Format("IsJumping: " + level.Player.Parameters.IsJumping.ToString()), new Vector2(xOffset, yOffset + (line++) * lineSpacing), Color.White);
            DrawShadowedString(spriteBatch, string.Format("IsOnGround: {0}", level.Player.Parameters.IsOnGround), new Vector2(xOffset, yOffset + (line++) * lineSpacing), Color.White);
            DrawShadowedString(spriteBatch, string.Format("Velocity: {0:####}, {1:####}", level.Player.Parameters.Velocity.X, level.Player.Parameters.Velocity.Y), new Vector2(xOffset, yOffset + (line++) * lineSpacing), Color.White);
            spriteBatch.End();
        }

        private void DrawPlayingLevel(GameTime gameTime)
        {
            spriteBatch.Begin();
            string lives = string.Format("lives: {0:000}", level.Player.Lives);
            string score = string.Format("score: {0:0000000}", level.Player.Score);
            DrawShadowedString(spriteBatch, lives, new Vector2(50, 0), Color.White);
            DrawShadowedString(spriteBatch, score, new Vector2(550, 0), Color.White);

            for(int i=0; i<level.Player.CollectedPowerups.Count; i++)
            {
                Powerup p = level.Player.CollectedPowerups[i];
                p.Parameters.Position = new Vector2(80+64*i, 100);
                p.Draw(gameTime, spriteBatch);
            }


            if (!level.Player.IsAlive && !level.Player.GameOver)
                DrawShadowedString(spriteBatch, "...press a key", new Vector2(300, 300), Color.White);
            spriteBatch.End();
        }

        private void DrawGameOver(GameTime gameTime)
        {
            spriteBatch.Begin();
            DrawShadowedString(spriteBatch, "game over", new Vector2(400, 300), Color.White);
            spriteBatch.End();
        }

        private void DrawLevelComplete(GameTime gameTime)
        {
            spriteBatch.Begin();
            DrawShadowedString(spriteBatch, "level completed!", new Vector2(300, 300), Color.White);
            spriteBatch.End();
        }


        private void DrawMenu(GameTime gameTime)
        {
            spriteBatch.Begin();
            int x = 400;
            int y = 300;
            for (int i = 0; i < menuStringItems.Count; i++)
            {
                DrawShadowedString(spriteBatch, menuStringItems[i], new Vector2(x, y + i * 40), Color.White);
            }

            DrawShadowedString(spriteBatch, "-->", new Vector2(x - 100, y + 40 * currentMenuIndex), Color.White);
            spriteBatch.End();
        }

        public static void DrawShadowedString(SpriteBatch batch, string value, Vector2 position, Color color)
        {
            if (Constants.Alpha == 1.0f)
            {
                Color shadow = new Color(Color.Black, 1f);
                Color shadow2 = new Color(Color.Black, 0.5f);
                batch.DrawString(font, value, position + new Vector2(6.0f, 6.0f), shadow2);
                for (int i = -1; i <= 1; i++)
                {
                    for (int j = -1; j <= 1; j++)
                    {
                        if (i == 0 && j == 0)
                            continue;
                        batch.DrawString(font, value, position + new Vector2(3 * j, 3 * i), shadow);
                    }
                }
            }
            batch.DrawString(font, value, position, new Color(color, Constants.Alpha));
        }
    }
}
